package cn.shadow.moulde;//

import com.esotericsoftware.reflectasm.MethodAccess;
import com.google.common.base.Preconditions;

import java.lang.reflect.Method;
import java.util.Arrays;

import io.netty.buffer.ByteBuf;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Handler {
    private static final Logger logger = LoggerFactory.getLogger(Handler.class);
    private static final int MIN_MSG_ID = 0;
    private static final int MAX_MSG_ID = 16383;
    private final int msgId;
    private final Object module;
    private final MethodAccess methodAccess;
    private final int methodIndex;
    private final Handler.ParamFinder paramFinder;
    private boolean isClosed;

    public Handler(MethodAccess methodAccess, Object module, Method method, AHandler handler) {
        this.msgId = handler.value();
        this.module = module;
        this.methodAccess = methodAccess;
        Preconditions.checkArgument(isValidMsgId(this.msgId), "%s 方法 %s 消息号不在有效范围!", new Object[]{this, method.getName()});
        Class<?>[] paramTypes = method.getParameterTypes();
        this.methodIndex = methodAccess.getIndex(method.getName(), paramTypes);
        if (ArrayUtils.isEmpty(paramTypes)) {
            this.paramFinder = null;
        } else {
            this.paramFinder = new Handler.ParamFinder(paramTypes);
        }

    }

    private static boolean isValidMsgId(int msgId) {
        return msgId >= 0 && msgId <= 16383;
    }


    public void handle(ByteBuf buffer, IModuleObjController controller) {
        if (this.isClosed()) {
            logger.debug("Handler.handle(), 已经被关闭，无法处理消息!{}", this);
        } else if (this.paramFinder == null) {
            this.methodAccess.invoke(this.module, this.methodIndex);
        } else {
            Object[] params = this.paramFinder.getParams(buffer, controller);
            this.methodAccess.invoke(this.module, this.methodIndex, params);
            Arrays.fill(params, null);
        }
    }

    public boolean isClosed() {
        return this.isClosed;
    }

    void doClose() {
        this.isClosed = true;
    }

    void doOpen() {
        this.isClosed = false;
    }

    public String toString() {
        return this.module + ", MsgId = " + this.msgId;
    }

    private static class ParamFinder {
        private static final int BUFFER_FLAG = -1;
        private static final int OBJ_CONTROLLER_FLAG = -2;
        private final int paramsCount;
        private final Class<?>[] paramTypes;
        private final int[] paramsIntTypes;
        private final ThreadLocal<Object[]> paramProvider;

        public ParamFinder(Class<?>[] paramTypes) {
            this.paramsCount = paramTypes.length;
            this.paramTypes = paramTypes;
            this.paramsIntTypes = new int[this.paramsCount];
            for (int idx = 0; idx < this.paramsCount; ++idx) {
                Class<?> paramType = paramTypes[idx];
                if (ByteBuf.class.isAssignableFrom(paramType)) {
                    this.paramsIntTypes[idx] = BUFFER_FLAG;
                } else if (IModuleObjController.class.isAssignableFrom(paramType)) {
                    this.paramsIntTypes[idx] = OBJ_CONTROLLER_FLAG;
                }
            }

            this.paramProvider = ThreadLocal.withInitial(() -> new Object[ParamFinder.this.paramsCount]);
        }


        public Object[] getParams(ByteBuf buffer, IModuleObjController controller) {
            Object[] params = this.paramProvider.get();

            for (int idx = 0; idx < this.paramsCount; ++idx) {
                params[idx] = this.getParam(this.paramsIntTypes[idx], buffer, controller);
            }

            return params;
        }

        private Object getParam(int flag, ByteBuf buffer, IModuleObjController controller) {
            if (flag == -1) {
                return buffer;
            }
            if (flag == -2) {
                return controller != null ? controller : controller.getModuleObj(flag);
            }
            return null;
        }
    }
}

